/*
*
* 版权所有 Copyleft(c) 2005
* 刘炯个人工作室 Sharp-Mark Personal Studio
*
* 允许自由使用，复制，修改，传播和贩卖此源程序及其文档，而不必支付任何费用。
* 以上的版权信息必须出现在每一份复制品中。同时这份版权信息和许可声明
* 也必须出现在支持文档中。刘炯个人工作室不对此软件适于那种具体
* 用途做出任何阐述。此代码只作为代码本身提供对其品质不做任何保证。
*
*/

#include "vectors.h"
#define CMATRIX_NUM 4

// 矩阵类
template<class T>
class CMatrix
{
public:

	CMatrix(){ SetZero(); }
	virtual ~CMatrix(){}

	// 数据访问
	T Get(int i, int j) const;						// 获得某一个元素的值
	void Set(int i, int j, T value);				// 设置某一个元素的值
	CVec4<T> GetVector(int index);					// 获取某一列的向量

	// 特殊矩阵
	void SetZero();									// 零矩阵
	void SetIdentity();								// 单位矩阵
	void SetProjections(T d);						// 投影矩阵

	// 操作符重载
	CMatrix& operator=(const CMatrix& rhs);
	CMatrix& operator+=(const CMatrix& rhs);
	CMatrix& operator-=(const CMatrix& rhs);
	CMatrix& operator*=(const T& rhs);
	CMatrix& operator*=(const CMatrix& rhs);
	const CMatrix operator*(const CMatrix& rhs) const;
	const CVec4<T> operator*(const CVec4<T>& rhs) const;
	const CMatrix operator*(const T& rhs) const;
	const CMatrix operator/(const T& rhs) const;
	const CMatrix operator-(const CMatrix& rhs) const;
	const CMatrix operator+(const CMatrix& rhs) const;
	bool operator==(const CMatrix& rhs) const;
	bool operator!=(const CMatrix& rhs) const;
	friend CMatrix operator*(const T& lhs, const CMatrix& rhs);

	// 转化
	void Translate(T x, T y, T z);						// 平移
	void Scale(T x, T y, T z);							// 缩放
	void Scale(T s) { Scale(s, s, s); }
	void XRotate(float angel);							// 绕X轴旋转
	void YRotate(float angel);							// 绕Y轴旋转
	void ZRotate(float angel);							// 绕Z轴旋转
	//void ChangeCoordinates();							// 坐标轴原点平移， 未实现...

protected:

	bool IsCoordinatesCurrent(int i, int y) const;		// 检验坐标是不是合法

protected:

	T _data[CMATRIX_NUM][CMATRIX_NUM];					// 私有成员，矩阵数据

	friend class CVec4<T>;
};

template<class T>
T CMatrix<T>::Get(int i, int j) const
{
	assert(IsCoordinatesCurrent(i, j));
	return _data[i][j];
}


template<class T>
void CMatrix<T>::Set(int i, int j, T value)
{
	assert(IsCoordinatesCurrent(i, j));
	_data[i][j] = value;
}

template<class T>
void CMatrix<T>::SetZero()
{
	for(int i = 0; i < CMATRIX_NUM; i++)
	{
		for(int j = 0; j < CMATRIX_NUM; j++)
		{
			_data[i][j] = static_cast<T>(0);
		}
	}
}

template<class T>
void CMatrix<T>::SetIdentity()
{
	for(int i = 0; i < CMATRIX_NUM; i++)
	{
		for(int j = 0; j < CMATRIX_NUM; j++)
		{
			_data[i][j] = static_cast<T>(i == j);
		}
	}
}


template<class T>
void CMatrix<T>::SetProjections(T d)
{
	SetIdentity();
	_data[0][0] = 1;
	_data[1][1] = 1;
	_data[3][2] = - static_cast<T>(static_cast<float>(1) / static_cast<float>(d));
	_data[3][3] = 1;
}

template<class T>
bool CMatrix<T>::IsCoordinatesCurrent(int i, int j) const
{
	if((i < CMATRIX_NUM && i >= 0) || (j < CMATRIX_NUM && j >= 0))
		return true;
	else
		return false;
}

template<class T>
CMatrix<T>& CMatrix<T>::operator=(const CMatrix& rhs)
{
	if(this == &rhs) return *this;

	for(int i = 0; i < CMATRIX_NUM; i++)
	{
		for(int j = 0; j < CMATRIX_NUM; j++)
		{
			(*this)._data[i][j] = rhs._data[i][j];
		}
	}

	return *this;
}

template<class T>
CMatrix<T>& CMatrix<T>::operator+=(const CMatrix& rhs)
{
	for(int i = 0; i < CMATRIX_NUM; i++)
	{
		for(int j = 0; j < CMATRIX_NUM; j++)
		{
			_data[i][j] += rhs._data[i][j];
		}
	}
	return *this;
}

template<class T>
CMatrix<T>& CMatrix<T>::operator-=(const CMatrix& rhs)
{
	for(int i = 0; i < CMATRIX_NUM; i++)
	{
		for(int j = 0; j < CMATRIX_NUM; j++)
		{
			_data[i][j] -= rhs._data[i][j];
		}
	}
	return *this;
}

template<class T>
CMatrix<T>& CMatrix<T>::operator*=(const T& rhs)
{
	for(int i = 0; i < CMATRIX_NUM; i++)
	{
		for(int j = 0; j < CMATRIX_NUM; j++)
		{
			_data[i][j] = rhs * _data[i][j];
		}
	}
	return *this;
}

template<class T>
CMatrix<T>& CMatrix<T>::operator*=(const CMatrix& rhs)
{
	for(int i = 0; i < CMATRIX_NUM; i++)
	{
		for(int j = 0; j < CMATRIX_NUM; j++)
		{
			_data[i][j] = _data[i][0] * rhs._data[0][j];
			for(int k = 1; k < CMATRIX_NUM; k++)
			{
				_data[i][j] += _data[i][k] * rhs._data[k][j];
			}
		}
	}
	return *this;
}

template<class T>
const CMatrix<T> CMatrix<T>::operator*(const CMatrix& rhs) const
{
	CMatrix<T> result;
	for(int i = 0; i < CMATRIX_NUM; i++)
	{
		for(int j = 0; j < CMATRIX_NUM; j++)
		{
			for(int k = 0; k < CMATRIX_NUM; k++)
			{
				result._data[i][j] += _data[i][k] * rhs._data[k][j];
			}
		}
	}
	return result;
}

template<class T>
const CVec4<T> CMatrix<T>::operator*(const CVec4<T>& rhs) const
{
	CVec4<T> result;
	for(int i = 0; i < CMATRIX_NUM; i++)
	{
		for(int j = 0; j < CMATRIX_NUM; j++)
		{
			result._data[i] += _data[i][j] * rhs._data[j];
		}
	}
	return result;
}

template<class T>
const CMatrix<T> CMatrix<T>::operator*(const T& rhs) const
{
	CMatrix<T> result;
	for(int i = 0; i < CMATRIX_NUM; i++)
	{
		for(int j = 0; j < CMATRIX_NUM; j++)
		{
			result._data[i][j] = _data[i][j] * rhs;
		}
	}
	return result;
}

template<class T>
const CMatrix<T> CMatrix<T>::operator/(const T& rhs) const
{
	CMatrix<T> result;
	for(int i = 0; i < CMATRIX_NUM; i++)
	{
		for(int j = 0; j < CMATRIX_NUM; j++)
		{
			result._data[i][j] = _data[i][j] / rhs;
		}
	}
	return result;
}

template<class T>
const CMatrix<T> CMatrix<T>::operator-(const CMatrix& rhs) const
{
	CMatrix result;
	for(int i = 0; i < CMATRIX_NUM; i++)
	{
		for(int j = 0; j < CMATRIX_NUM; j++)
		{
			result._data[i][j] = _data[i][j] - rhs._data[i][j];
		}
	}
	return result;
}

template<class T>
const CMatrix<T> CMatrix<T>::operator+(const CMatrix& rhs) const
{
	CMatrix result;
	for(int i = 0; i < CMATRIX_NUM; i++)
	{
		for(int j = 0; j < CMATRIX_NUM; j++)
		{
			result._data[i][j] = _data[i][j] + rhs._data[i][j];
		}
	}
	return result;

}

template<class T>
bool CMatrix<T>::operator==(const CMatrix& rhs) const
{
	for(int i = 0; i < CMATRIX_NUM; i++)
	{
		for(int j = 0; j < CMATRIX_NUM; j++)
		{
			if (_data[i][j] != rhs._data[i][j]) return false;
		}
	}
	return true;
}

template<class T>
bool CMatrix<T>::operator!=(const CMatrix& rhs) const
{
	return !((*this) == rhs);
}

template<class T>
CMatrix<T> operator*(const T& lhs, const CMatrix<T>& rhs)
{
	return rhs * lhs;
}

template<class T>
void CMatrix<T>::Translate(T x, T y, T z)
{
	CMatrix<T> t;
	t.SetIdentity();
	t._data[0][3] = x;
	t._data[1][3] = y;
	t._data[2][3] = z;

	*this = t * (*this);		// 不可写做(*this) * t，顺序在矩阵乘法中很重要
}

template<class T>
void CMatrix<T>::Scale(T x, T y, T z)
{
	CMatrix<T> t;
	t.SetIdentity();
	t._data[0][0] = x;
	t._data[1][1] = y;
	t._data[2][2] = z;

	*this = t * (*this);
}

template<class T>
void CMatrix<T>::XRotate(float angel)
{
	CMatrix<T> rx;
	rx.SetIdentity();
	rx._data[1][1] = static_cast<T>(std::cos(angel));
	rx._data[1][2] = static_cast<T>(-std::sin(angel));
	rx._data[2][1] = static_cast<T>(std::sin(angel));
	rx._data[2][2] = static_cast<T>(std::cos(angel));
	*this = rx * (*this);
}

template<class T>
void CMatrix<T>::YRotate(float angel)
{
	CMatrix<T> ry;
	ry.SetIdentity();
	ry._data[0][0]= static_cast<T>(std::cos(angel));
	ry._data[0][2]= static_cast<T>(std::sin(angel));
	ry._data[2][0]= static_cast<T>(-std::sin(angel));
	ry._data[2][2]= static_cast<T>(std::cos(angel));
	*this = ry * (*this);
}

template<class T>
void CMatrix<T>::ZRotate(float angel)
{
	CMatrix<T> rz;
	rz.SetIdentity();
	rz._data[0][0]= static_cast<T>(std::cos(angel));
	rz._data[0][1]= static_cast<T>(-std::sin(angel));
	rz._data[1][0]= static_cast<T>(std::sin(angel));
	rz._data[1][1]= static_cast<T>(std::cos(angel));
	*this = rz * (*this);
}

template<class T>
CVec4<T> CMatrix<T>::GetVector(int index)
{
	return CVec4(_data[0][index], _data[1][index], _data[2][index], _data[3][index]);
}